package com.hdu;

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import lombok.Data;
import lombok.RequiredArgsConstructor;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class TestMain {


    private static final Map<String, Metrics> metricsMap = new HashMap<>();


    /**
     *  mock 指标元信息数据
     */
    static {
        metricsMap.put("m", new Metrics("m", "a + b"));
        metricsMap.put("a", new Metrics("a", null));
        metricsMap.put("b", new Metrics("b", "c + d"));
        metricsMap.put("c", new Metrics("c", null));
        metricsMap.put("d", new Metrics("d", null));
    }


    public static void main(String[] args) {
        System.out.println(calMetrics("m"));
    }


    public static Object calMetrics(String metricsName) {
        Metrics metrics = mockGetMetricsByMetricsName(metricsName);
        if (metrics.getCalExpression() == null) {
            // 如果是叶子指标 直接计算
            return mockQueryMetricsValue(metricsName);
        } else {
            // 不是叶子指标

            // 表达式
            String calExpression = metrics.getCalExpression();
            Expression compileExpression = AviatorEvaluator.compile(calExpression);
            // 提取所有依赖的指标
            List<String> dependencyMetricsNames = compileExpression.getVariableNames();
            // 递归计算所依赖的指标
            Map<String, Object> dependencyMetricsValues = dependencyMetricsNames
                    .stream()
                    .collect(Collectors.toMap(
                            dependencyMetricsName -> dependencyMetricsName,
                            TestMain::calMetrics
                    ));

            // 计算
            return compileExpression.execute(dependencyMetricsValues);
        }
    }


    /**
     * mock 通过指标名字查询指标元信息
     *
     * @param metricsName
     * @return
     */
    public static Metrics mockGetMetricsByMetricsName(String metricsName) {
        return metricsMap.get(metricsName);
    }


    /**
     * mock 通过指标名字查询指标值
     *
     * @param metricsName
     * @return
     */
    public static Object mockQueryMetricsValue(String metricsName) {
        switch (metricsName) {
            case "a":
                return 1;
            case "c":
                return 3;
            case "d":
                return 4;
            default:
                return null;
        }
    }
}


@Data
@RequiredArgsConstructor
class Metrics {
    /**
     * 指标名字
     */
    private final String metricsName;
    /**
     * 复合指标的计算表达式
     */
    private final String calExpression;
}
